package com.yvan.dev2word.swagger2doc.utils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.yvan.dev2word.swagger2doc.vo.ApiDocVo;
import com.yvan.dev2word.swagger2doc.vo.ApiInterfaceVo;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;

public class Swagger2Utils {

    public static void main(String[] args) {

    }

    public static ApiDocVo findInterface(String json) {
        assert !StringUtils.isEmpty(json);
        json = json.replace("$", "");
        Map map = JSONObject.parseObject(json, Map.class);
        // 基础访问root路径
        String basePath = (String) map.get("basePath");
        // 基础访问root路径
        String host = (String) map.get("host");
        // 解析方法
        ArrayList<ApiInterfaceVo> apiInterfaceVos = new ArrayList<>();
        if (map.get("paths") == null) {
            // 没有接口
            return null;
        }
        //获取所有请求路径的容器
        JSONArray tagsContainer = (JSONArray) map.get("tags");

        Map<String, List<ApiInterfaceVo>> tagsInterfaceMap = tagsHandle(tagsContainer);
        //获取所有请求路径的容器
        JSONObject pathsContainer = (JSONObject) map.get("paths");
        //获取所有model的容器
        JSONObject modelContainer = (JSONObject) map.get("definitions");

        Set<String> pathUrlSet = pathsContainer.keySet();
        for (String pathUrl : pathUrlSet) {
            //初始化一个请求参容器
            List<ApiInterfaceVo.RequestBean> requestBeans = new ArrayList<>();

            JSONObject jsonObject = (JSONObject) pathsContainer.get(pathUrl);
            //获取get方法的方法
            JSONObject getJsonObject = (JSONObject) jsonObject.get("get");
            get(apiInterfaceVos, modelContainer, pathUrl, requestBeans, getJsonObject);

            //获取post方法的方法
            JSONObject postJsonObject = (JSONObject) jsonObject.get("post");
            post(apiInterfaceVos, modelContainer, pathUrl, requestBeans, postJsonObject);
            //获取post方法的方法
            JSONObject putJsonObject = (JSONObject) jsonObject.get("put");
            put(apiInterfaceVos, modelContainer, pathUrl, requestBeans, putJsonObject);
            //获取post方法的方法
            JSONObject delJsonObject = (JSONObject) jsonObject.get("delete");
            delete(apiInterfaceVos, modelContainer, pathUrl, requestBeans, delJsonObject);
        }
        interfaceClassify(tagsInterfaceMap, apiInterfaceVos);

        ApiDocVo apiDocVo = new ApiDocVo();
        apiDocVo.setBaseUrl(host);
        apiDocVo.setRootPath(basePath);
        apiDocVo.setInterfaceVoList(apiInterfaceVos);
        apiDocVo.setMap(tagsInterfaceMap);
        return apiDocVo;
    }

    private static void interfaceClassify(Map<String, List<ApiInterfaceVo>> map, List<ApiInterfaceVo> list) {
        for (ApiInterfaceVo apiInterfaceVo : list) {

            String tagName = apiInterfaceVo.getTagName();
            List<ApiInterfaceVo> apiInterfaceVos = map.get(tagName);
            if (apiInterfaceVos == null) {
                apiInterfaceVos = new ArrayList<>();
            }
            apiInterfaceVos.add(apiInterfaceVo);
            map.put(tagName, apiInterfaceVos);
        }
    }

    private static void get(List<ApiInterfaceVo> apiInterfaceVos, JSONObject modelContainer, String pathUrl,
                            List<ApiInterfaceVo.RequestBean> requestBeans, JSONObject getJsonObject) {
        if (getJsonObject == null) {
            return;
        }

        //获取所有入参属性,由于get方法都是表单传参就是formdata,所以所有的参数都在这个`parameters`里面
        Object parameters = getJsonObject.get("parameters");
        if (parameters != null) {
            JSONArray paramArray = (JSONArray) parameters;

            //遍历所有参数
            for (Object paramObj : paramArray) {
                JSONObject param = (JSONObject) paramObj;
                //由于get方法有两种传参模式,路径传参和表单传参,所有要分开获取入参数值
                String in = (String) param.get("in");
                //解析表单传参属性
                inOfQuery(requestBeans, param, in);
                //解析路径传参属性
                inOfPath(requestBeans, param, in);
            }
        }
        Map<String, Object> responseMap = new HashMap<>();

        Object responseVos = getJsonObject.get("responses");
        // 响应处理
        responseHandle(modelContainer, responseMap, responseVos);

        //美化json
        String responseJson = formatJson(responseMap);

        //封装Vo数据
        ApiInterfaceVo apiInterfaceVo = new ApiInterfaceVo();

        apiInterfaceVo.setUri(pathUrl);
        String summary = (String) getJsonObject.get("summary");
        summary = summary.replace("&", "And");
        apiInterfaceVo.setExplain(summary);
        apiInterfaceVo.setAgreement("Get");
        apiInterfaceVo.setRequestList(requestBeans);
        apiInterfaceVo.setResponseJson(responseJson);
        JSONArray tags = (JSONArray) getJsonObject.get("tags");
        if (!tags.isEmpty()) {
            String tagName = (String) tags.get(0);
            apiInterfaceVo.setTagName(tagName);
        }
        apiInterfaceVos.add(apiInterfaceVo);
    }

    private static void post(List<ApiInterfaceVo> apiInterfaceVos, JSONObject modelContainer, String pathUrl,
                             List<ApiInterfaceVo.RequestBean> requestBeans, JSONObject postJsonObject) {
        if (postJsonObject == null) {
            return;
        }
        Map<String, Object> requestMap = new HashMap<>();
        Object parameters = postJsonObject.get("parameters");

        if (parameters != null) {
            JSONArray paramArray = (JSONArray) parameters;
            for (Object paramObj : paramArray) {
                JSONObject param = (JSONObject) paramObj;

                String in = (String) param.get("in");
                inOfQuery(requestBeans, param, in);
                inOfPath(requestBeans, param, in);
                inOfBody(modelContainer, requestMap, param, in);


            }
        }
        Map<String, Object> responseMap = new HashMap<>();

        Object responseVos = postJsonObject.get("responses");
        // 响应处理
        responseHandle(modelContainer, responseMap, responseVos);

        //美化json
        String responseJson = formatJson(responseMap);
        //美化json
        String requestJson = formatJson(requestMap);
        //封装数据
        ApiInterfaceVo apiInterfaceVo = new ApiInterfaceVo();
        apiInterfaceVo.setUri(pathUrl);
        String summary = (String) postJsonObject.get("summary");
        summary = summary.replace("&", "And");
        apiInterfaceVo.setExplain(summary);apiInterfaceVo.setAgreement("Post");
        apiInterfaceVo.setRequestList(requestBeans);
        apiInterfaceVo.setRequestJson(requestJson);
        apiInterfaceVo.setResponseJson(responseJson);
        JSONArray tags = (JSONArray) postJsonObject.get("tags");
        if (!tags.isEmpty()) {
            String tagName = (String) tags.get(0);
            apiInterfaceVo.setTagName(tagName);
        }
        apiInterfaceVos.add(apiInterfaceVo);
    }

    private static void put(List<ApiInterfaceVo> apiInterfaceVos, JSONObject modelContainer, String pathUrl,
                            List<ApiInterfaceVo.RequestBean> requestBeans, JSONObject putJsonObject) {
        if (putJsonObject == null) {
            return;
        }
        Map<String, Object> requestMap = new HashMap<>();
        Object parameters = putJsonObject.get("parameters");

        if (parameters != null) {
            JSONArray paramArray = (JSONArray) parameters;
            for (Object paramObj : paramArray) {
                JSONObject param = (JSONObject) paramObj;

                String in = (String) param.get("in");
                inOfQuery(requestBeans, param, in);
                inOfPath(requestBeans, param, in);
                inOfBody(modelContainer, requestMap, param, in);
            }
        }
        Map<String, Object> responseMap = new HashMap<>();

        Object responseVos = putJsonObject.get("responses");
        // 响应处理
        responseHandle(modelContainer, responseMap, responseVos);
        //美化json
        String requestJson = formatJson(requestMap);
        //美化json
        String responseJson = formatJson(responseMap);

        //封装数据
        ApiInterfaceVo apiInterfaceVo = new ApiInterfaceVo();
        apiInterfaceVo.setUri(pathUrl);
        String summary = (String) putJsonObject.get("summary");
        summary = summary.replace("&", "And");
        apiInterfaceVo.setExplain(summary);apiInterfaceVo.setAgreement("Put");
        apiInterfaceVo.setRequestList(requestBeans);
        apiInterfaceVo.setRequestJson(requestJson);
        apiInterfaceVo.setResponseJson(responseJson);
        JSONArray tags = (JSONArray) putJsonObject.get("tags");
        if (!tags.isEmpty()) {
            String tagName = (String) tags.get(0);
            apiInterfaceVo.setTagName(tagName);
        }
        apiInterfaceVos.add(apiInterfaceVo);
    }

    private static void delete(List<ApiInterfaceVo> apiInterfaceVos, JSONObject modelContainer, String pathUrl,
                               List<ApiInterfaceVo.RequestBean> requestBeans, JSONObject delJsonObject) {
        if (delJsonObject == null) {
            return;
        }
        Object parameters = delJsonObject.get("parameters");

        if (parameters != null) {
            JSONArray paramArray = (JSONArray) parameters;
            for (Object paramObj : paramArray) {
                JSONObject param = (JSONObject) paramObj;
                String in = (String) param.get("in");
                inOfPath(requestBeans, param, in);
            }
        }
        Map<String, Object> responseMap = new HashMap<>();

        Object responseVos = delJsonObject.get("responses");
        // 响应处理
        responseHandle(modelContainer, responseMap, responseVos);

        //美化json
        String responseJson = formatJson(responseMap);

        //封装数据
        ApiInterfaceVo apiInterfaceVo = new ApiInterfaceVo();
        apiInterfaceVo.setUri(pathUrl);
        String summary = (String) delJsonObject.get("summary");
        summary = summary.replace("&", "And");
        apiInterfaceVo.setExplain(summary);apiInterfaceVo.setAgreement("Delete");
        apiInterfaceVo.setRequestList(requestBeans);
        apiInterfaceVo.setResponseJson(responseJson);
        JSONArray tags = (JSONArray) delJsonObject.get("tags");
        if (!tags.isEmpty()) {
            String tagName = (String) tags.get(0);
            apiInterfaceVo.setTagName(tagName);
        }
        apiInterfaceVos.add(apiInterfaceVo);
    }

    private static void responseHandle(JSONObject modelContainer, Map<String, Object> responseMap, Object responseVos) {
        if (responseVos != null) out:{
            JSONObject responses = (JSONObject) responseVos;
            JSONObject successObj = (JSONObject) responses.get("200");
            JSONObject schema = (JSONObject) successObj.get("schema");
            if (schema == null) {
                break out;
            }
            String modelName;
            String originalRef = (String) schema.get("originalRef");
            String ref = (String) schema.get("ref");
            if (!StringUtils.isEmpty(ref)) {
                modelName = getModelName(ref);
            } else if (!StringUtils.isEmpty(originalRef)) {
                modelName = originalRef;
            } else if (StringUtils.isEmpty(originalRef) && StringUtils.isEmpty(ref)) {
                break out;
            } else {
                break out;
            }
            recursionResponseAttr(modelContainer, responseMap, modelName, null, "");
        }
    }

    private static void recursionResponseAttr(JSONObject modelContainer, Map<String, Object> parentMap,
                                              String modelName, String mapKey, String objectType) {
        Object modelObj = modelContainer.get(modelName);
        Map<String, Object> currentMap = new HashMap<>();
        if (modelObj != null) {
            JSONObject modelJsonObj = (JSONObject) modelObj;
            JSONObject propertiesObj = (JSONObject) modelJsonObj.get("properties");
            if (propertiesObj == null) {
                return;
            }
            Set<String> keyNameSet = propertiesObj.keySet();
            for (String attrName : keyNameSet) {
                JSONObject attribute = (JSONObject) propertiesObj.get(attrName);
                //如果是对象则不需要type
                String attrRel = attribute.get("ref") == null ? null : (String) attribute.get("ref");
                if (!StringUtils.isEmpty(attrRel)) {
                    String attrModelName = getModelName(attrRel);
                    recursionResponseAttr(modelContainer, currentMap, attrModelName, attrName, "object");
                    continue;
                }
                //如果是对象则需要type和items
                String attrType = attribute.get("type") == null ? "object" : (String) attribute.get("type");
                if ("array".equals(attrType)) {
                    JSONObject attrItems = attribute.get("items") == null ? null : (JSONObject) attribute.get("items");
                    assert attrItems != null;
                    String attrItemsRef = attrItems.get("ref") == null ? null : (String) attrItems.get("ref");
                    if (!StringUtils.isEmpty(attrItemsRef)) {
                        String attrModelName = getModelName(attrItemsRef);
                        if (modelName.equals(attrModelName)) {
                            continue;
                        }
                        recursionResponseAttr(modelContainer, currentMap, attrModelName, attrName, "array");
                        continue;
                    }
                }
                String description = attribute.get("description") == null ? "暂未定义" : (String) attribute.get("description");

                currentMap.put(attrName, attrType + " | " + description);
            }
            if (mapKey == null) {
                parentMap.putAll(currentMap);
            } else {
                if (objectType.equals("object")) {
                    parentMap.put(mapKey, currentMap);
                } else if (objectType.equals("array")) {
                    if (modelName.contains("Entry")) {
                        parentMap.put(mapKey, currentMap);
                    } else {
                        ArrayList<Map<String, Object>> list = new ArrayList<>();
                        list.add(currentMap);
                        parentMap.put(mapKey, list);
                    }
                }
            }
        }
    }

    private static void recursionRequestAttr(JSONObject modelContainer, Map<String, Object> parentMap,
                                             String modelName, String mapKey, String objectType) {
        Object modelObj = modelContainer.get(modelName);
        Map<String, Object> currentMap = new HashMap<>();
        if (modelObj != null) {
            JSONObject modelJsonObj = (JSONObject) modelObj;
            JSONObject propertiesObj = (JSONObject) modelJsonObj.get("properties");
            Set<String> keyNameSet = propertiesObj.keySet();
            for (String attrName : keyNameSet) {
                JSONObject attribute = (JSONObject) propertiesObj.get(attrName);
                //如果是对象则不需要type
                String attrRel = attribute.get("ref") == null ? null : (String) attribute.get("ref");
                if (!StringUtils.isEmpty(attrRel)) {
                    String attrModelName = getModelName(attrRel);
                    recursionRequestAttr(modelContainer, currentMap, attrModelName, attrName, "object");
                    continue;
                }
                //如果是对象则需要type和items
                String attrType = attribute.get("type") == null ? "object" : (String) attribute.get("type");
                if (attrType.equals("array")) {
                    JSONObject attrItems = attribute.get("items") == null ? null : (JSONObject) attribute.get("items");
                    assert attrItems != null;
                    String attrItemsRef = attrItems.get("ref") == null ? null : (String) attrItems.get("ref");
                    if (!StringUtils.isEmpty(attrItemsRef)) {
                        String attrModelName = getModelName(attrItemsRef);
                        recursionRequestAttr(modelContainer, currentMap, attrModelName, attrName, "array");
                        continue;
                    }
                }

                String description = attribute.get("description") == null ? "暂未定义" : (String) attribute.get("description");

                currentMap.put(attrName, attrType + " | " + description);
            }
            if (mapKey == null) {
                parentMap.putAll(currentMap);
            } else {
                if (objectType.equals("object")) {
                    parentMap.put(mapKey, currentMap);
                } else if (objectType.equals("array")) {
                    ArrayList<Map<String, Object>> list = new ArrayList<>();
                    list.add(currentMap);
                    parentMap.put(mapKey, list);
                }
            }
        }
    }

    private static String getModelName(String ref) {
        return ref.substring(ref.lastIndexOf("/") + 1);
    }

    private static String formatJson(Map<String, Object> map) {
        String mapStr = JSONObject.toJSONString(map);
        return JsonFormatTool.formatJson(mapStr);
    }

    private static void inOfBody(JSONObject modelContainer, Map<String, Object> requestMap, JSONObject param, String in) {
        if (!in.equals("body")) {
            return;
        }
        JSONObject schema = (JSONObject) param.get("schema");
        String ref = (String) schema.get("ref");
        if (ref == null) {
            Object itemsObj = schema.get("items");
            if (itemsObj == null) {
                String type = (String) schema.get("type");
                String name = (String) param.get("name");
                String description = (String) param.get("description");
                requestMap.put(name + " | " + description, type);
                return;
            } else {
                ref = (String) ((JSONObject) itemsObj).get("ref");
            }
        }
        String modelName = getModelName(ref);
        recursionRequestAttr(modelContainer, requestMap, modelName, null, "");
    }

    private static void inOfQuery(List<ApiInterfaceVo.RequestBean> requestBeans, JSONObject param, String in) {
        if (!in.equals("query")) {
            return;
        }
        ApiInterfaceVo.RequestBean requestBean = new ApiInterfaceVo.RequestBean();
        requestBean.setName((String) param.get("name"));
        requestBean.setDescription((String) param.get("description"));
        requestBean.setType((String) param.get("type"));
        requestBean.setRequired((Boolean) param.get("required"));
        requestBeans.add(requestBean);
    }

    private static void inOfPath(List<ApiInterfaceVo.RequestBean> requestBeans, JSONObject param, String in) {
        if (!in.equals("path")) {
            return;
        }
        ApiInterfaceVo.RequestBean requestBean = new ApiInterfaceVo.RequestBean();
        requestBean.setName((param.get("name")) + "(路径传参)");
        requestBean.setDescription((String) param.get("description"));
        requestBean.setType((String) param.get("type"));
        requestBean.setRequired(true);
        requestBeans.add(requestBean);
    }

    private static Map<String, List<ApiInterfaceVo>> tagsHandle(JSONArray tagsContainer) {
        HashMap<String, List<ApiInterfaceVo>> tagsMap = new HashMap<>();
        for (Object tag : tagsContainer) {
            JSONObject tagObj = (JSONObject) tag;
            String name = (String) tagObj.get("name");
            tagsMap.put(name, new ArrayList<>());
        }
        return tagsMap;
    }

    /**
     * 通过文件获取json
     *
     * @param file json文件
     * @return json
     */
    public static String getJson(File file) {
        try {
            FileReader fileReader = new FileReader(file);
            Reader reader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);
            int ch = 0;
            StringBuilder sb = new StringBuilder();
            while ((ch = reader.read()) != -1) {
                sb.append((char) ch);
            }
            fileReader.close();
            reader.close();
            return sb.toString();
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 通过swagger doc url获取json
     *
     * @param apiUrl swagger doc url
     * @return json
     */
    public static String getJson(String apiUrl) {
        try {
            RestTemplate restTemplate = new RestTemplate();
            return restTemplate.getForObject(apiUrl, String.class);
        } catch (Exception e) {
            return null;
        }
    }
}
